/**
  *  \file ex9.c  **  modified by Mark Burns
  *  \brief PDK Example Program -- Galactic Bank Wire Transfer
  *
  *  This program demonstrates the use of the command message parser,
  *  also for a pretty complicated command syntax.
  *
  *  This implements a very simple wire transfer between planets. I
  *  mean, we have had cashless money transfers in the ending 20th
  *  century, I'd be rather surprised if a "space age" civilisation
  *  does not have such a thing :-) I guess it is best run in
  *  auxhost1.
  *
  *  Players send a command `bank: transfer 100 mc from 123 to 456'
  *  ("x bank: ...." in PHost 3.4a and below), and this add-on will
  *  move the cash instantly. You can also write the `transfer'
  *  commands into `bankN.txt' (where N is your player number). Thanks
  *  PDK it also supports the "old-style" command format ("x
  *  transfer...", "x bank transfer..."), with all its disadvantages.
  *
  *  The player who gives the command must own the sending planet;
  *  ownership of the receiving planet doesn't matter (i.e. you can
  *  move cash to unowned planets, or send a gift to your allies).
  *
  *  In addition, this understands the "help" command.
  *
  *  Hacked together in about an hour by Stefan Reuther.
  *
  *  Extensions that come to mind:
  *  - transfer limits (amount, distance)
  *  - better logging
  *  - send the receiver of the cash a message
  *
  *  This file was written by Stefan Reuther in 2002/2003. You may
  *  copy and distribute this file freely as long as you retain this
  *  notice and explicitly document any changes you make to this file
  *  (along with your name and date) before distributing modified
  *  versions.
  *
  *  This is an example program for instructional purposes only. It is
  *  not guaranteed to work correctly, or even to necessarily compile
  *  on all systems. You use this program at your own risk.
  */

#include <phostpdk.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <string.h>

#define false 0
#define true 1

FILE *bankfile;
FILE *inifile;

float fInterest = 0.00;         /** Default interest value is 0. used if config file not found. */

unsigned long lBalance[RACE_NR] = {0};
unsigned long lPrevBalance[RACE_NR] = {0};
unsigned long lInterest[RACE_NR] = {0};
unsigned long lTotalDeposit[RACE_NR] = {0};
unsigned long lTotalSend[RACE_NR] = {0};
unsigned long lTotalWithdraw[RACE_NR] = {0};
unsigned long lTotalReceive[RACE_NR] = {0};

static char* sProgName;         /**< Name of the program, for help screen.  */
static char sWarnings = 0;      /**< Nonzero to send warnings to screen, not subspace.  */
static char sVerbose = 0;       /**< Nonzero to be more verbose. */
static char sReadonly = 0;      /**< Nonzero for "dry-run" (read-only). */

/** Lookup tables for base64 encode/decode for fast translation. */
static const char base64EncTable[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                     "abcdefghijklmnopqrstuvwxyz"
                                     "0123456789+/";
static const char base64DecTable[256] = {
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,62, 00,00,00,63,
                   52,53,54,55, 56,57,58,59, 60,61,00,00, 00,00,00,00,
                    0, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
                   15,16,17,18, 19,20,21,22, 23,24,25,00, 00,00,00,00,
                   00,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
                   41,42,43,44, 45,46,47,48, 49,50,51,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                   00,00,00,00, 00,00,00,00, 00,00,00,00, 00,00,00,00,
                  };



/**************************************************************************************************************/
/** Decode Base64 string into a new buffer and return it.  Memory for the new buffer is allocated. */
char *B64_Decode(const char *string)
{
    int stringLen = strlen(string);
    char *result = malloc((ceil(stringLen/4.0)*3)+1);
    int t,o;

    if(result == NULL) {
        return result;
    }

    t=o=0;
    do {
        result[o++] = ((base64DecTable[(int)string[t]] << 2 ) & 0xfc ) |
                ((base64DecTable[(int)string[t+1]] >> 4 ) & 0x03 );

        result[o++] = ((base64DecTable[(int)string[t+1]] << 4 ) & 0xf0 ) |
                ((base64DecTable[(int)string[t+2]] >> 2 ) & 0x0f );

        result[o++] = ((base64DecTable[(int)string[t+2]] << 6 ) & 0xc0 ) |
                ((base64DecTable[(int)string[t+3]]));

        t+=4;
    } while(t < (int)strlen(string));

    return result;
}

/**************************************************************************************************************/
/** Takes string and encodes it in base 64. Returns the new string in allocated memory. */
char *B64_Encode(const char *string)
{
    int stringLen = strlen(string);
    char *result = malloc((int)(ceil(stringLen/3.0)*4)+1);
    int t,o;

    if(result == NULL) {
        return result;
    }

    t=o=0;
    do {
        result[o++] = base64EncTable[(string[t] >> 2) & 0x3f];

        result[o++] = base64EncTable[((string[t] << 4) & 0x30) |
                               ((string[t+1] >> 4) & 0x0f)];

        if(t+1<stringLen)
            result[o++] = base64EncTable[((string[t+1] << 2) & 0x3c) |
                                 ((string[t+2] >> 6) & 0x03)];
        else
            result[o++] = '=';

        if(t+2<stringLen)
            result[o++] = base64EncTable[((string[t+2]) & 0x3f)];
        else
            result[o++] = '=';

        t+=3;
    } while(t < (int)strlen(string));

    /* Null terminate */
    result[o++] = '\0';

    return result;
}

/**************************************************************************************************************/
/** Help screen. */
void
Help(void)
{
    fprintf(stderr,
            "VGA Planets Bank Addon\n\n"
            "usage: %s [-hwvr] gamedir [rootdir]\n\n"
            " -h   this help screen\n"
            " -w   show complaints as warnings (default: subspace messages)\n"
            " -v   verbose\n"
            " -r   read-only\n",
            sProgName);
    exit(1);
}

/**************************************************************************************************************/
/** Process a complaint. This is the CommandComplain_Func. */
void
ProcessComplaint(Uns16 pRace, const char* pLine, const char* pReason, void* pData)
{
    if (sWarnings)
        ComplainWithWarningMessage(pRace, pLine, pReason, pData);
    else
        ComplainWithSubspaceMessage(pRace, pLine, pReason, pData);
}

/**************************************************************************************************************/
/** True iff strings are equal, ignoring case. */
Boolean
StringEq(const char* a, const char* b)
{
    while (*a && toupper((unsigned char)*a) == toupper((unsigned char)*b))
        ++a, ++b;
    if (*a || *b)
        return False;
    else
        return True;
}

/**************************************************************************************************************/
/** Convert string to integer and MemFree it.
    \param pRace   [in] race, for error messages
    \param pLine   [in] whole line, for error messages
    \param pString [in] the string
    \param pValue  [out] value
    \param pMin,pMax [in] allowed range */
Boolean
StringToInt(Uns16 pRace, const char* pLine,
            char* pString, Uns32* pValue, Uns32 pMin, Uns32 pMax)
{
    char* p;
    unsigned long l = strtoul(pString, &p, 10);
    if (*pString && !*p && l >= pMin && l <= pMax) {
        MemFree(pString);
        *pValue = l;
        return True;
    } else {
        char buffer[50];
        /* normally we should fill in the correct value for pData
           here. This program doesn't use that parameter. */
        sprintf(buffer, "Invalid number: '%.20s'", pString);
        ProcessComplaint(pRace, pLine, buffer, 0);
        MemFree(pString);
        return False;
    }
}

/**************************************************************************************************************/
/** Process a "deposit" command. This takes the string apart. */
void
ProcessDepositCommand(Uns16 pRace, const char* pArgs, const char* pWholeLine)
{
    static const char delim[] = " \t";
    Boolean bAmountAll = false;
    Boolean bFromAll = false;
    Uns16 ix;
    Uns16 iStartPlanet, iEndPlanet;
    Uns32 lAmount, lTempAmount;
    Uns32 lSrc;
    char* p;

    /* syntax is: deposit 123 [mc] from 123 */
    p = GetToken(&pArgs, delim);
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected amount to deposit.", 0);
        return;
    }
    if (StringEq(p, "*"))
        bAmountAll = true;
    else if (!StringToInt(pRace, pWholeLine, p, &lAmount, 0, 1000000000))
        return;

    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "mc")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (p && StringEq(p, "from")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected sender planet Id.", 0);
        return;
    }
    if (StringEq(p, "*"))
        bFromAll = true;
    else if (!StringToInt(pRace, pWholeLine, p, &lSrc, 1, PLANET_NR))
        return;

    if ((p = GetToken(&pArgs, delim)) != 0) {
        MemFree(p);
        ProcessComplaint(pRace, pWholeLine, "Trailing garbage.", 0);
    }

    if (bFromAll == true) {
        iStartPlanet = 1;
        iEndPlanet = PLANET_NR;
    } else {
        iStartPlanet = lSrc;
        iEndPlanet = lSrc;
    }

    for (ix = iStartPlanet; ix <= iEndPlanet; ix++) {
        lTempAmount = lAmount;

        /* now we have all parameters. Validate them. */
        if (IsPlanetExist(ix) && PlanetOwner(ix) == pRace) {
	    if ((bAmountAll == true) || (PlanetCargo(ix, CREDITS) < lAmount))
                lTempAmount = PlanetCargo(ix, CREDITS);
            if (lTempAmount > 0) {
                PutPlanetCargo(ix, CREDITS, PlanetCargo(ix, CREDITS) - lTempAmount);
                lBalance[pRace-1] = lBalance[pRace-1] + lTempAmount;
                lTotalDeposit[pRace-1] = lTotalDeposit[pRace-1] + lTempAmount;
                if (sVerbose)
                    Info("Player %d deposited %ld mc from %d to their global account.", pRace, lTempAmount, ix);
            }
        } else if (bFromAll == false) {
            ProcessComplaint(pRace, pWholeLine, "You don't own the sending planet.", 0);
            return;
        }
    }	
}

/**************************************************************************************************************/
/** Process a "send" command. This takes the string apart. */
void
ProcessSendCommand(Uns16 pRace, const char* pArgs, const char* pWholeLine)
{
    static const char delim[] = " \t";
    Boolean bAmountAll = false;
    Uns32 lAmount;
    Uns32 lDest;
    char* p;
    char message[128];

    /* syntax is: send 123 [mc] to 123 */
    p = GetToken(&pArgs, delim);
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected amount to transfer.", 0);
        return;
    }
    if (StringEq(p, "*"))
        bAmountAll = true;
    else if (!StringToInt(pRace, pWholeLine, p, &lAmount, 0, 1000000000))
        return;

    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "mc")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "to")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected receiver Race Id.", 0);
        return;
    }
    if (!StringToInt(pRace, pWholeLine, p, &lDest, 1, RACE_NR))
        return;
    if ((p = GetToken(&pArgs, delim)) != 0) {
        MemFree(p);
        ProcessComplaint(pRace, pWholeLine, "Trailing garbage.", 0);
    }

    /* now we have all parameters. Validate them. */
    if (!PlayerIsActive(lDest)) {
        ProcessComplaint(pRace, pWholeLine, "Race is not an active player.", 0);
        return;
    }

    if ((bAmountAll == true) || (lBalance[pRace-1] < lAmount))
        lAmount = lBalance[pRace-1];

    /* Don't do the send if you are trying to send it to yourself. */
    if (lDest != pRace && lAmount > 0) {
        lBalance[pRace-1] = lBalance[pRace-1] - lAmount;
        lBalance[lDest-1] = lBalance[lDest-1] + lAmount;
        lTotalSend[pRace-1] = lTotalSend[pRace-1] + lAmount;
        lTotalReceive[lDest-1] = lTotalReceive[lDest-1] + lAmount;
        sprintf(message,
           "<<< Bank Account Activity >>>\015\015"
           "Your account has received:\015"
           "%ld mc\015"
           "from Player %d!\015",
           lAmount, pRace);
        WriteAUXHOSTMessage(lDest, message);
        if (sVerbose)
            Info("Player %d sent %ld mc to player %ld.", pRace, lAmount, lDest);
    }
}

/**************************************************************************************************************/
/** Process a "transfer" command. This takes the string apart. */
void
ProcessTransferCommand(Uns16 pRace, const char* pArgs, const char* pWholeLine)
{
    static const char delim[] = " \t";
    Boolean bAmountAll = false;
    Boolean bFromAll = false;
    Uns16 ix;
    Uns16 iStartPlanet, iEndPlanet;
    Uns32 lAmount, lTempAmount;
    Uns32 lSrc, lDest;
    char* p;

    /* syntax is: transfer 123 [mc] from 123 to 123 */
    p = GetToken(&pArgs, delim);
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected amount to transfer.", 0);
        return;
    }
    if (StringEq(p, "*"))
        bAmountAll = true;
    else if (!StringToInt(pRace, pWholeLine, p, &lAmount, 0, 1000000000))
        return;

    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "mc")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (p && StringEq(p, "from")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected sender planet Id.", 0);
        return;
    }
    if (StringEq(p, "*"))
        bFromAll = true;
    else if (!StringToInt(pRace, pWholeLine, p, &lSrc, 1, PLANET_NR))
        return;

    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "to")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected receiver planet Id.", 0);
        return;
    }
    if (!StringToInt(pRace, pWholeLine, p, &lDest, 1, PLANET_NR))
        return;
    if ((p = GetToken(&pArgs, delim)) != 0) {
        MemFree(p);
        ProcessComplaint(pRace, pWholeLine, "Trailing garbage.", 0);
    }

    if (bFromAll == true) {
        iStartPlanet = 1;
        iEndPlanet = PLANET_NR;
    } else {
        iStartPlanet = lSrc;
        iEndPlanet = lSrc;
    }

    for (ix = iStartPlanet; ix <= iEndPlanet; ix++) {
        lTempAmount = lAmount;

        /* now we have all parameters. Validate them. */
        if (!IsPlanetExist(lDest) || PlanetOwner(lDest) != pRace) {
            ProcessComplaint(pRace, pWholeLine, "You don't own the receiving planet.", 0);
            return;
        }

        if (IsPlanetExist(ix) && PlanetOwner(ix) == pRace) {
	    if ((bAmountAll == true) || (PlanetCargo(ix, CREDITS) < lAmount))
                lTempAmount = PlanetCargo(ix, CREDITS);
            if (lTempAmount > 0) {
                PutPlanetCargo(ix, CREDITS, PlanetCargo(ix, CREDITS) - lTempAmount);
                PutPlanetCargo(lDest, CREDITS, PlanetCargo(lDest, CREDITS) + lTempAmount);
                if (sVerbose)
                    Info("Player %d transferred %ld mc from %d to %ld.", pRace, lTempAmount, ix, lDest);
            }
        } else if (bFromAll == false) {
            ProcessComplaint(pRace, pWholeLine, "You don't own the sending planet.", 0);
            return;
        }
    }	
}

/**************************************************************************************************************/
/** Process a "withdraw" command. This takes the string apart. */
void
ProcessWithdrawCommand(Uns16 pRace, const char* pArgs, const char* pWholeLine)
{
    static const char delim[] = " \t";
    Boolean bAmountAll = false;
    Uns32 lAmount;
    Uns32 lDest;
    char* p;

    /* syntax is: withdraw 123 [mc] to 123 */
    p = GetToken(&pArgs, delim);
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected amount to transfer.", 0);
        return;
    }
    if (StringEq(p, "*"))
        bAmountAll = true;
    else if (!StringToInt(pRace, pWholeLine, p, &lAmount, 0, 1000000000))
        return;

    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "mc")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    p = GetToken(&pArgs, delim);
    if (p && StringEq(p, "to")) {
        MemFree(p);
        p = GetToken(&pArgs, delim);
    }
    if (!p) {
        ProcessComplaint(pRace, pWholeLine, "Expected receiver planet Id.", 0);
        return;
    }
    if (!StringToInt(pRace, pWholeLine, p, &lDest, 1, PLANET_NR))
        return;
    if ((p = GetToken(&pArgs, delim)) != 0) {
        MemFree(p);
        ProcessComplaint(pRace, pWholeLine, "Trailing garbage.", 0);
    }

    /* now we have all parameters. Validate them. */
    if (!IsPlanetExist(lDest) || PlanetOwner(lDest) != pRace) {
        ProcessComplaint(pRace, pWholeLine, "You don't own the receiving planet.", 0);
        return;
    }

    if ((bAmountAll == true) || (lBalance[pRace-1] < lAmount))
        lAmount = lBalance[pRace-1];

    if (lAmount > 0) {
        lBalance[pRace-1] = lBalance[pRace-1] - lAmount;
        PutPlanetCargo(lDest, CREDITS, PlanetCargo(lDest, CREDITS) + lAmount);
        lTotalWithdraw[pRace-1] = lTotalWithdraw[pRace-1] + lAmount;
        if (sVerbose)
            Info("Player %d withdrew %ld mc to %ld.", pRace, lAmount, lDest);
    }
}

/**************************************************************************************************************/
/** Command handler. This is the CommandReader_Func. */
Boolean
ProcessCommand(Uns16 pRace, const char* pCommand, const char* pArgs,
               const char* pWholeLine, void* pData)
{
    (void) pData;               /* we don't use it */
    
    /* help command */
    if (StringEq(pCommand, "help")) {
        WriteAUXHOSTMessage(pRace,
                            "(-h000)<<< Bank Help >>>\015"
                            "The Bank understands these commands:\015"
                            "- 'help'\015"
                            "  this help text.\015"
                            "- 'deposit N mc from A'\015"
                            "  deposit money to account.\015"
                            "  * can be used for N or A.\015"
                            "- 'send N mc to A'\015"
                            "  send money to race A account.\015"
                            "  * can be used for N.\015"
                            "- 'transfer N mc from A to B'\015"
                            "  transfer money between planets.\015"
                            "  * can be used for N or A.\015"
                            "- 'withdraw N mc to A'\015"
                            "  withdraw money from account.\015"
                            "  * can be used for N.\015\015"
                            "Prefix commands with 'bank:'. Example:\015"
                            "bank: transfer 100 mc from 132 to 367\015");
        if (sVerbose)
            Info("Player %d asked for help.", pRace);
        return True;
    }

    /* deposit command */
    else if (StringEq(pCommand, "deposit")) {
        ProcessDepositCommand(pRace, pArgs, pWholeLine);
        return True;
    }

    /* send command */
    else if (StringEq(pCommand, "send")) {
        ProcessSendCommand(pRace, pArgs, pWholeLine);
        return True;
    }

    /* transfer command */
    else if (StringEq(pCommand, "transfer")) {
        ProcessTransferCommand(pRace, pArgs, pWholeLine);
        return True;
    }

    /* withdraw command */
    else if (StringEq(pCommand, "withdraw")) {
        ProcessWithdrawCommand(pRace, pArgs, pWholeLine);
        return True;
    }

    return False;
}

/**************************************************************************************************************/
/** Initialize Bank account balances.  Read from data file.  If file doesn't exist
    all balances are set to zero. */
void
InitializeBank(void)
{
    unsigned int iTempRace;
    char strTemp[32];
    int x;

    Info("Initializing Bank.");

    /** Initialize all values of the bank arrays to zero before begining. */
    for (x = 1; x <= RACE_NR; x++) {
        lBalance[x-1] = 0;
        lInterest[x-1] = 0;
        lTotalDeposit[x-1] = 0;
        lTotalSend[x-1] = 0;
        lTotalWithdraw[x-1] = 0;
        lTotalReceive[x-1] = 0;
    }

    /** Read bank account balances in from data file to balance array. */
    bankfile = OpenInputFile("bankdata.hst", GAME_DIR_ONLY | TEXT_MODE | NO_MISSING_ERROR);
    if (bankfile != NULL) {
        Info("Reading account balances from file.");
        while (!feof(bankfile)) {
            fscanf(bankfile, "%u=%s", &iTempRace, strTemp);
            lBalance[iTempRace-1] = strtoul(B64_Decode(strTemp), NULL, 10);
        }
        fclose(bankfile);
    } else {
        Info("Couldn't open bankdata.hst file for reading.");
        Info("Using default account balances of 0.");
    }

    /** Set Previous Balance now that we have read the balances in. */
    for (x = 1; x <= RACE_NR; x++) {
        lPrevBalance[x-1] = lBalance[x-1];
    }

    return;
}

/**************************************************************************************************************/
/** Open config file and read interest value, then calculate the interest earned on previous balance
    and add it to the current balance. */
void
CalculateInterest(void)
{
    int x;

    inifile = OpenInputFile("bank.cfg", GAME_DIR_ONLY | TEXT_MODE | NO_MISSING_ERROR);
    if (inifile != NULL) {
	int rv;

        Info("Reading interest setting from config file.");
        do {
            rv = fscanf(inifile, "Interest=%f", &fInterest);
        } while (rv == 0);
        fclose(inifile);
    } else {
        Info("Couldn't open bank.cfg file for reading.");
        Info("Using default interest value of 0.00%.");
    }


    Info("Computing and adding interest to accounts.");
    for (x = 1; x <= RACE_NR; x++) {
        if (PlayerIsActive(x)) {
            lInterest[x-1] = lPrevBalance[x-1] * (fInterest / 100);
            lBalance[x-1] = lBalance[x-1] + lInterest[x-1];
        }
    }

    return;
}

/**************************************************************************************************************/
/** Send out a message to every player in the game containing bank activity for the turn. */
void
SendBankStatements(void)
{
    int x;
    char message[420];

    Info("Sending out player bank account statements.");
    for (x = 1; x <= RACE_NR; x++) {
        if (PlayerIsActive(x)) {
            sprintf(message,
                "<<< Bank Account Statement >>>\015"
                "Statement for Turn %d:\015\015"
                "Previous Balance:    %10ld mc\015"
                "Interest (%6.2f%%): +%10ld mc\015"
                "Total Deposits:     +%10ld mc\015"
                "Total Withdrawls:   -%10ld mc\015"
                "Total Sent:         -%10ld mc\015"
                "Total Received:     +%10ld mc\015"
                "==================================\015"
                "CURRENT BALANCE      %10ld mc\015"
                "==================================\015",
                TurnNumber(), lPrevBalance[x-1], fInterest, lInterest[x-1], lTotalDeposit[x-1],
                lTotalWithdraw[x-1], lTotalSend[x-1], lTotalReceive[x-1], lBalance[x-1]);
            WriteAUXHOSTMessage(x, message);
        }
    }

    return;
}

/**************************************************************************************************************/
/** Write bank account balances out to file. */
void
WriteBankData(void)
{
    char strTemp[12];
    int x;

    Info("Writing account balances to bankdata.hst file.");
    bankfile = OpenOutputFile("bankdata.hst", GAME_DIR_ONLY | TEXT_MODE);
    for (x = 1; x <= RACE_NR; x++) {
        if (PlayerIsActive(x)) {
            sprintf(strTemp, "%ld", lBalance[x-1]);
            fprintf(bankfile, "%d=%s\n", x, B64_Encode(strTemp));
        }
    }
    fclose(bankfile);

    return;
}

/**************************************************************************************************************/
/** Main. */
int
main(int argc, char** argv)
{
    int c;
    sProgName = argv[0];

    while ((c = pgetopt(argc, argv, "hvwr")) != EOF) {
        if (c == 'h')
            Help();
        else if (c == 'v')
            sVerbose = 1;
        else if (c == 'w')
            sWarnings = 1;
        else if (c == 'r')
            sReadonly = 1;
        else
            ErrorExit("Invalid option '%c'.", c);
    }

    if (poptind < argc)
        gGameDirectory = argv[poptind++];
    if (poptind < argc)
        gRootDirectory = argv[poptind++];
    if (poptind < argc)
        ErrorExit("Too many parameters.");

    /* Here comes da fun */
    InitPHOSTLib();

    gLogFile = OpenOutputFile("bank.log", GAME_DIR_ONLY | TEXT_MODE);

    if (!ReadGlobalData())
        ErrorExit("Unable to read global data.");
    if (!ReadHostData())
        ErrorExit("Unable to read host data.");

    InitializeBank();
    CalculateInterest();

    Info("Starting to process bank commands.");
    CommandFileReader(0 /* any race does */,
                      ProcessCommand,
                      ProcessComplaint,
                      "bank",
                      "bank%d.txt",
                      0 /* private data */);
    Info("Finished processing bank commands.");


    /* write out everything */
    if (!sReadonly) {
        if (!WriteHostData()) {
            ErrorExit("Unable to write back host data.");
        } else {
            WriteBankData();
            SendBankStatements();
        }
    }

    Info("Bank finished successfully.");

    if (gLogFile != NULL) {
        fclose(gLogFile);
    }

    FreePHOSTLib();
    return 0;
}

